﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using Ninject;
using Example.Data.Context.Interfaces;
using Example.Base.Interfaces.Repositories;
using System.Data.Entity;
using System.Linq.Expressions;
using Example.Data.Validators;
using Example.Base.Entities;
using Example.Base.Interfaces.DataSources;

namespace Example.Data.Repositories
{
    public class Repository<TEntity>: IRepository<TEntity> where TEntity: BaseEntity
    {
        [Inject]
        public IDataContext Context { get; set; }

        /// <summary>
        /// Get entities from repository
        /// </summary>
        /// <param name="filter">Filter</param>
        /// <param name="orderBy">Order by property</param>
        /// <param name="includeProperties"></param>
        /// <returns></returns>
        public virtual IQueryable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            var query = Context.Set<TEntity>().AsQueryable();

            // Apply filter
            if (filter != null)
            {
                query = query.Where(filter);
            }

            // Apply includes
            foreach (var includeProperty in includeProperties.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            // Apply order by if need
            if (orderBy != null)
            {
                return orderBy(query);
            }
            else
            {
                return query;
            }
        }


        /// <summary>
        /// Find entity by key
        /// </summary>
        /// <param name="id">Identificator</param>
        /// <returns>Entity</returns>
        public virtual TEntity GetById(object id)
        {
            //throw new NotImplementedException();
            return Context.Set<TEntity>().Find(id);
        }

        /// <summary>
        /// Add new entity to repository
        /// </summary>
        /// <param name="entity">New entity</param>
        public virtual void Insert(TEntity entity)
        {
            Context.Set<TEntity>().Add(entity);
            Context.SaveChanges();
        }

        /// <summary>
        /// Updated entity in repositoy
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual void Update(TEntity entity)
        {
            AttachIsNotAttached(entity);
            Context.Entry(entity).State = System.Data.EntityState.Modified;            
            Context.SaveChanges();
        }

        /// <summary>
        /// Remove entity from rpository
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual void Delete(TEntity entity)
        {
            AttachIsNotAttached(entity);
            Context.Set<TEntity>().Remove(entity);
            Context.Entry(entity).State = EntityState.Deleted;
            Context.SaveChanges();
        }

        public IEnumerable<object> GetValidationModelErrors()
        {
            return Context.GetValidationErrors();
        }

        protected void AttachIsNotAttached(TEntity entity)
        {
            if (Context.Entry(entity).State == System.Data.EntityState.Detached)
            {
                var attached = Context.Set<TEntity>().Local.Where(x => x.Id == entity.Id).FirstOrDefault();

                if (attached != null)
                {
                    Context.Entry(attached).State = EntityState.Detached;
                }

                Context.Set<TEntity>().Attach(entity);
            }
        }

        [Inject]
        public IDatabase DataSource { get; set; }
    }
}
